/* * * * M A P V I S I O N   L I C E N S E  *****************************************************************
 *
 * This MapVision software is © Filip Zavadil 2013. It is licensed under the LGPL license.
 * This MapVision software dynamically links to unmodified Qt5 Library.
 *
 * The Qt5 Library is © 2013 Qt® and/or its subsidiary(-ies), and is licensed under the
 * GNU Lesser General Public License version 2.1 with Qt® LGPL exception version 1.1.
 * Qt5 library is free software; you can redistribute it and/or modify it under the terms of the
 * GNU Lesser General Public License, version 2.1, as published by the Free Software Foundation.
 * Qt5 library is provided “AS IS”, without WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
 * IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
 *
 * As an additional permission to the GNU Lesser General Public License version 2.1, the object
 * code form of a “work that uses the Library” may incorporate material from a header file that is
 * part of the Library. You may distribute such object code under terms of your choice,
 * provided that: (i) the header files of the Library have not been modified; and (ii)
 * the incorporated material is limited to numerical parameters, data structure layouts, accessors,
 * macros, inline functions and templates; and (iii) you comply with the terms of Section 6 of the
 * GNU Lesser General Public License version 2.1.
 *
 * Moreover, you may apply this exception to a modified version of the Library, provided that such
 * modification does not involve copying material from the Library into the modified Library’s header
 * files unless such material is limited to (i) numerical parameters; (ii) data structure layouts;
 * (iii) accessors; and (iv) small macros, templates and inline functions of five lines or less in length.
 *
 * Furthermore, you are not required to apply this additional permission to a modified version of the Library.
 *
 * You should have received a copy of the GNU Lesser General Public License along with this package;
 * if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

 * The source code for Qt 5.X SDK is available from Qt Project here:
 * http://download.qt-project.org/official_releases/qtcreator/2.7/2.7.0/qt-creator-2.7.0-src.zip.
 *
 * ****************************** M A P V I S I O N    S O F T W A R E  ************************************
 *
 * Author:       Filip Zavadil
 * Description:  Software for displaying and exporting map tiles.
 * The sources and executables of MapVision software are available:
 *    - from project hosting on CodePlex.com :   http://mapvision.codeplex.com
 *    - from GIT repositories on Gitorious.org:  https://gitorious.org/mapvision
 * Visit MapVision fanpage on Facebook.com:      https://www.facebook.com/pages/Mapvision/631827753513935
 *
 * *********************************************************************************************************
*/

#include "layertilesonline.h"
#include <QStandardPaths>
LayerTILESonline::LayerTILESonline(QMainWindow * mainWindow_) : Layer(mainWindow_)
{

    index = 0;



    dialog = new DialogLayerTILESonline(mainWindow_,this);
    dialog->setPage(0);
    if (dialog->exec()){
        init();
    } else {
        this->deleteLater();
        this->~Layer();
    }
}

LayerTILESonline::LayerTILESonline(QMainWindow *mainWindow_, Layer::layerType type_, QString name_, QString source_, QPointF lonlat_, int zoom_, int zValue_, int opacity_)
    : Layer(mainWindow_)
{
    index = 0;

    setLayerType(type_);
    setLayerName(name_);
    setOrigin(lonlat_,zoom_);
    setOpacity(double(opacity_)/100.0);
    setSource(source_);
    setZvalue(zValue_);
    init();

}

void LayerTILESonline::init()
{
    getMapScene()->addItem(layerGroup);

    serverPrefix = getSubDomains(this->getSource());

    QRegExp regExp("\\$\\{sub\\[.*\\]\\}");
    if (serverPrefix.size()==0) setSource(getSource().remove(regExp));
    connect(&manager, SIGNAL(finished(QNetworkReply*)),
            this, SLOT(downloadFinished(QNetworkReply*)));

    connect(getMapView(),SIGNAL(sceneCleared()),this,SLOT(zoomHasChanged(int)));
    connect(this,SIGNAL(zoomIsChanging(int)),this,SLOT(zoomHasChanged(int)));

    // testing ......................
    QNetworkDiskCache *diskCache = new QNetworkDiskCache(this);
    //QString location = QDesktopServices::storageLocation(QDesktopServices::CacheLocation);
    QString location = QStandardPaths::standardLocations(QStandardPaths::CacheLocation)[0];
    diskCache->setCacheDirectory(location);
    manager.setCache(diskCache);
    // testing ........................


    // insert item into layer list
    QListWidgetItem *item = new QListWidgetItem(this->getLayerName());
    item->setCheckState(Qt::Checked);
    item->setData(Qt::UserRole,getId());
    item->setIcon(QIcon("images/buttons/tilesOnline.svg"));

    getListWidget()->insertItem(getZvalue(),item);
    setZvalue(this->getListWidget()->count()-1);
    layerGroup->setZValue(getZvalue());

    emit sendMessage("<code>"+tr("Layer ")+this->getLayerName()+tr(" was loaded!")+"</code><br>"  );

    getMapView()->update();

    getListWidget()->update();
    errors=0;
}



QImage LayerTILESonline::getTile(QPoint tile, int zoom)
{
    QRegExp regExp("\\$\\{sub\\[.*\\]\\}");
    QImage image;
    QString urlString = getSource();
    urlString.remove(" ");


    if (serverPrefix.size()>0) urlString.replace(regExp,serverPrefix[index]);

    urlString.replace("${z}",QString::number(zoom));
    urlString.replace("${x}",QString::number(tile.x()));
    urlString.replace("${y}",(YaxisOrigin==ZavaProj::TOP) ? QString::number(zavaProj.convertYaxis(tile.y(),zoom)): QString::number(tile.y()));

    QUrl url(urlString);

    QNetworkRequest request(url);
    request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
    QNetworkReply * reply = manager.get(request);

    // synchronous downloading :)
    QEventLoop loop;
    QObject::connect(reply, SIGNAL(readyRead()), &loop, SLOT(quit()));
    loop.exec();

    // loop is quit when the reply emit readyRead();
    image.loadFromData(reply->readAll());
    return image;
}


void LayerTILESonline::mapRectChanged(QRect rect, int zoom)
{
    if (isRendering()){
        int cache=0;
        for (int i=rect.left()-cache;i<=rect.right()+cache;i++){
            for (int j=rect.bottom()-cache;j<=rect.top()+cache;j++){
                int j2=(YaxisOrigin==ZavaProj::TOP) ? zavaProj.convertYaxis(j,zoom): j;

                if (j2>=0 && i>=0){
                    QPoint pos = zavaProj.pointForTile(QPoint(i,j),zoom);

                    QRegExp regExp("\\$\\{sub\\[.*\\]\\}");
                    QString urlString = getSource();
                    urlString.remove(" ");
                    if (serverPrefix.size()>0)  urlString.replace(regExp,serverPrefix[index]);
                    urlString.replace("${z}",QString::number(zoom));
                    urlString.replace("${x}",QString::number(i));
                    urlString.replace("${y}",QString::number(j2));
                    QUrl url(urlString);

                    if (!sources.contains(this->tileFormat2string(pos.x(),pos.y()))){
                        sources << this->tileFormat2string(pos.x(),pos.y());
                        this->download(url,pos);
                        index = (serverPrefix.size()-1 > index) ? index+1 : 0;
                        setTilesToLoad(1);
                    }
                }
            }
        }
    }
}


void LayerTILESonline::download(QUrl url, QPoint pos)
{
    QNetworkRequest request(url);
    request.setAttribute(QNetworkRequest::User, QVariant(pos));
    request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
    replyList << manager.get(request);
}


void LayerTILESonline::downloadFinished(QNetworkReply * reply)
{
    QImage image;
    QPoint pos=reply->request().attribute(QNetworkRequest::User).toPoint();

    if (!image.loadFromData(reply->readAll()) || reply->error()!=QNetworkReply::NoError || !sources.contains(tileFormat2string(pos.x(),pos.y()))){
        if (reply->error()==5){setTilesToLoad(-1);return;}
        sources.removeOne(this->tileFormat2string(pos.x(),pos.y()));
        networkErrors << reply->url().toString();
        if (errors<=0) emit sendMessage( "<span style=\"color: #680000;\" >"+ tr("Layer ")+"<b>"+this->getLayerName()+"</b> :  "+reply->errorString()+"</span>" );
        errors++;

    } else {

        QGraphicsPixmapItem * tile =(getMapScene()->addPixmap(QPixmap::fromImage(image,Qt::DiffuseAlphaDither)));
        tile->setPos(pos);
        tile->setData(Qt::UserRole,reply->url().toString());
        layerGroup->addToGroup(tile);
        items.insert(reply->url().toString(),tile);
        replyList.removeOne(reply);
    }
    setTilesToLoad(-1);
}

void LayerTILESonline::zoomHasChanged(int)
{
    foreach (QNetworkReply * reply, replyList) {
        reply->close();
    }
    replyList.clear();
    errors=0;
}


QStringList LayerTILESonline::getSubDomains(QString source)
{
    // https://mts${sub[1,2,3]}.google.com/vt/lyrs=m@195000000&hl=cs&src=app&x=${x}&y=${y}&z=${z}&s=Galileo
    QRegExp regExp("\\$\\{sub\\[.*\\]\\}");

    if (!source.contains(regExp)) return QStringList();

    int indexFirst = source.indexOf(regExp)+6;

    if (indexFirst == 0) return QStringList();

    int indexLast = source.indexOf("]}");

    if (indexLast<=indexFirst) return QStringList();

    QString subDomains = source.mid(indexFirst,indexLast-indexFirst);

    return subDomains.split(",");
}

QString LayerTILESonline::tileFormat2string(int x, int y)
{
    return QString::number(x)+"_"+QString::number(y);
}
